package com.demo.service.impl;

import com.demo.constant.CacheConstants;
import com.demo.excetion.ServiceException;
import com.demo.utils.GenNumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * redis锁
 * @author gxd
 * @since 2025/6/9
 */
@Component
public class TestService {

    @Autowired
    private RedisTemplate redisTemplate;

    /*@Autowired
    private RedissonClient redissonClient;*/

    /**
     * 锁函件号
     * 生成一个函件唯一序列号: 省首字母缩写第一个字母 + 行政区划前六位编码 + 八位年月日 + 六位自增号
     * 问题：实现不了后拿不到锁的进行等待，并设置超时时间
     * @param province 省份
     * @param regionCode 行政区划
     * @return 函数号
     */
    public String lockLetterNumber(String province, String regionCode) {
        // 获取函件号前部分：省首字母缩写第一个字母 + 行政区划前六位编码 + 八位年月日
        String letterStr = GenNumberUtils.genLetterStr(province, regionCode);
        // 锁key
        String lockKey = CacheConstants.LOCK_KEY + letterStr;
        // 序号key
        String seqKey = CacheConstants.SEQ_KEY + letterStr;
        // 函件号
        String letterNumber = null;
        try {
            // 尝试获取锁（持有时间5秒）
            boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "LOCKED", 5, TimeUnit.SECONDS);
            // 获取锁失败
            if (!locked) {
                throw new ServiceException("获取函件编号失败，请稍后重试");
            }
            // 获取当前序号，redis incr 原子自增
            long currentNumber = redisTemplate.opsForValue().increment(seqKey, 1);
            // 生成函件编号
            letterNumber = letterStr + String.format("%06d", currentNumber);
            return letterNumber;
        } finally {
            // 释放锁
            redisTemplate.delete(lockKey);
        }
    }

    /**
     * 锁方法
     * 生成一个函件唯一序列号: 省首字母缩写第一个字母 + 行政区划前六位编码 + 八位年月日 + 六位自增号
     * 问题：并发下不好使；后拿不到锁的进行等待，并设置超时时间
     */
    public String lockMethod(String province, String regionCode) {
        // 获取函件号前部分：省首字母缩写第一个字母 + 行政区划前六位编码 + 八位年月日
        String letterStr = GenNumberUtils.genLetterStr(province, province);
        // 锁key：方法+参数
        String lockKey = CacheConstants.LOCK_KEY + "getLetterNumber:" + province + ":" + regionCode;
        // 序号key
        String seqKey = CacheConstants.SEQ_KEY + letterStr;
        // 函件号
        String letterNumber = null;
        try {
            // 尝试获取锁（持有时间5秒）
            boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "LOCKED", 5, TimeUnit.SECONDS);
            // 获取锁失败
            if (!locked) {
                throw new ServiceException("获取函件编号失败，请稍后重试");
            }
            // 获取当前序号，redis incr 原子自增
            long currentNumber = redisTemplate.opsForValue().increment(seqKey, 1);
            // 生成函件编号
            letterNumber = letterStr + String.format("%06d", currentNumber);
            return letterNumber;
        } finally {
            redisTemplate.delete(lockKey);
        }
    }


    /**
     * Redis 分布式锁 + 自增序号 + 等待重试
     * 微服务集群分布式情况下 如何生成系统唯一函件编号 ，后拿不到锁的进行等待，并设置超时时间
     */
    public String lockLetterNumberWait(String province, String regionCode) {
        // 获取函件号前部分：省首字母缩写第一个字母 + 行政区划前六位编码 + 八位年月日
        String letterStr = GenNumberUtils.genLetterStr(province, regionCode);
        // 锁的key
        String lockKey = CacheConstants.LOCK_KEY + letterStr;
        // 序号key
        String seqKey = CacheConstants.SEQ_KEY + letterStr;
        // 开始时间
        long startTime = System.currentTimeMillis();
        // 最大等待时间：5秒
        long waitTimeout = 5000;
        // 函件号
        String letterNumber = null;

        try {
            // 尝试获取锁（带超时 + 循环等待）
            while (true) {
                // 尝试获取锁（持有时间10秒）
                boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "LOCKED", 10, TimeUnit.SECONDS);
                // 成功获取锁
                if (locked) {
                    break;
                }
                // 检测是否超时
                if (System.currentTimeMillis() - startTime > waitTimeout) {
                    throw new ServiceException("获取函件编号超时，请稍后重试");
                }
                // 等待100ms后重试
                Thread.sleep(100);
            }
            // 获取当前序号，redis incr 原子自增
            long currentNumber = redisTemplate.opsForValue().increment(seqKey, 1);
            // 生成函件编号
            letterNumber = letterStr + String.format("%06d", currentNumber);
            return letterNumber;
        } catch (InterruptedException e) {
            // 中断当前线程
            Thread.currentThread().interrupt();
            throw new ServiceException("获取函件编号失败");
        } finally {
            // 释放锁
            redisTemplate.delete(lockKey);
        }
    }

  /*  *//**
     * Redisson 分布式锁
     * 微服务集群分布式情况下 如何生成系统唯一函件编号 ，后拿不到锁的进行等待，并设置超时时间
     *//*
    public String redissonLock(String province, String regionCode) {
        // 获取函件号前部分：省首字母缩写第一个字母 + 行政区划前六位编码 + 八位年月日
        String letterStr = GenNumberUtils.genLetterStr(province, regionCode);
        // 锁key
        String lockKey = CacheConstants.LOCK_KEY + letterStr;
        // 序号key
        String seqKey = CacheConstants.SEQ_KEY + letterStr;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            // 尝试加锁，看门狗机制，会自动判断是否超时，是否重试，等待时间是5秒，持有时间是10秒
            boolean locked = lock.tryLock(5, 10, TimeUnit.SECONDS);
            // 获取锁失败
            if (!locked) {
                throw new ServiceException("获取函件编号超时，请稍后重试");
            }
            // 获取当前序号，redis incr 原子自增
            long currentNumber = redissonClient.getAtomicLong(seqKey).incrementAndGet();
            // 生成函件编号
            return letterStr + currentNumber;
        } catch (InterruptedException e) {
            // 中断当前线程
            Thread.currentThread().interrupt();
            throw new ServiceException("获取函件编号失败");
        } finally {
            lock.unlock();
        }
    }*/
}
